package com.onlyxiahui.common.action.description.handler.impl.method;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.core.MethodParameter;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.onlyxiahui.common.action.description.DocumentContext;
import com.onlyxiahui.common.action.description.bean.MethodData;
import com.onlyxiahui.common.action.description.bean.ModuleData;
import com.onlyxiahui.common.action.description.bean.ParameterData;
import com.onlyxiahui.common.action.description.bean.PropertyData;
import com.onlyxiahui.common.action.description.enums.annotation.MethodRequest;
import com.onlyxiahui.common.action.description.enums.data.JsonDataType;
import com.onlyxiahui.common.action.description.enums.data.ParameterType;
import com.onlyxiahui.common.action.description.enums.type.ActionType;
import com.onlyxiahui.common.action.description.handler.ParameterHandler;
import com.onlyxiahui.common.action.description.handler.impl.BaseMethodHandler;
import com.onlyxiahui.common.action.description.util.ActionAnnotationUtil;

/**
 * Description <br>
 * Date 2020-01-07 16:37:50<br>
 * 
 * @author XiaHui [onlovexiahui@qq.com]<br>
 * @since 1.0.0
 */

public class ActionMappingMethodHandler extends BaseMethodHandler {

	@Override
	public boolean support(DocumentContext context, ModuleData moduleData, Method method) {
		Annotation[] as = method.getAnnotations();
		boolean hasActionMapping = ActionAnnotationUtil.hasAnnotation(MethodRequest.ActionMapping.getAnnotation(), as);
		return hasActionMapping;
	}

	@Override
	public MethodData handle(DocumentContext context, ModuleData moduleData, Method method) {

		Annotation[] mas = method.getAnnotations();
		Object value = ActionAnnotationUtil.getValue(MethodRequest.ActionMapping.getAnnotation(), "value", mas);

		List<String> paths = new ArrayList<>();
		if (value instanceof String) {
			paths.add(value.toString());
		}
		if (value instanceof String[]) {
			String[] values = (String[]) value;
			if (null != values && values.length > 0) {
				for (String p : values) {
					paths.add(p);
				}
			}
		}

		MethodData md = this.create(context, moduleData, method, paths, true);
		md.setPaths(paths);
		md.addMethodType(ActionType.post.type());
		List<ParameterData> parameters = createParameters(context, moduleData, md, method);
		md.setParameters(parameters);

		return md;
	}

	public List<ParameterData> createParameters(
			DocumentContext context,
			ModuleData moduleData,
			MethodData md,
			Method method) {
		List<ParameterData> parameters = new ArrayList<>();
		int size = method.getParameterCount();
		Map<String, PropertyData> rootPathMap = new HashMap<>(size);
		Map<String, PropertyData> pathMap = new HashMap<>(size);
		setRootParameters(context, moduleData, md, method, rootPathMap, pathMap);
		for (PropertyData pd : rootPathMap.values()) {
			ParameterData data = new ParameterData(ParameterType.node.type(), pd);
			parameters.add(data);
		}
		return parameters;
	}

	public void setRootParameters(
			DocumentContext context,
			ModuleData moduleData,
			MethodData md,
			Method method,
			Map<String, PropertyData> rootPathMap,
			Map<String, PropertyData> pathMap) {

		Class<?> classType = method.getDeclaringClass();
		MethodParameter[] mps = initMethodParameters(method, classType);
		for (MethodParameter parameter : mps) {
			for (ParameterHandler ph : context.getParameterHandlers()) {
				if (context.getIgnoreJudgeBox().ignore(context, method, parameter, "")) {
					continue;
				}
				if (ph.support(context, moduleData, md, method, parameter)) {
					// Class<?> clazz = mp.getParameterType();
					ParameterData data = ph.handle(context, moduleData, md, method, parameter);
					PropertyData pd = data.getProperty();
					if (null != pd) {
						String name = pd.getName();
						if (null != name && !name.isEmpty()) {
							setRoot(rootPathMap, pathMap, pd, name);
						} else {
							// parameters.add(pd);
						}
					}
				}
			}
		}
	}

	public void setRoot(Map<String, PropertyData> rootPathMap, Map<String, PropertyData> pathMap, PropertyData pd, String pathName) {
		JSONObject o = new JSONObject();
		JSONPath.set(o, pathName, "");
		Set<String> keySet = o.keySet();
		for (String key : keySet) {
			Object v = o.get(key);
			if (v instanceof JSONObject) {
				PropertyData superData = pathMap.get(key);
				if (null == superData) {
					superData = new PropertyData();

					String name = key;
					String description = "";
					String type = JsonDataType.Object.type();
					String className = "map";

					superData.setName(name);
					superData.setType(type);
					superData.setDescription(description);
					superData.setClassName(className);

					pathMap.put(key, superData);
				}

				PropertyData data = rootPathMap.get(key);
				if (null == data) {
					data = superData;
					rootPathMap.put(key, data);
				}
				JSONObject jo = (JSONObject) v;
				setNode(key, key, pathMap, jo, superData, pd);
			} else {
				pd.setName(key);
				rootPathMap.put(key, pd);
			}
		}
	}

	public void setNode(String superKey, String superPath, Map<String, PropertyData> pathMap, JSONObject o, PropertyData superData, PropertyData data) {

		Set<String> keySet = o.keySet();
		for (String key : keySet) {
			String path = superPath + "." + key;
			Object v = o.get(key);
			if (v instanceof JSONObject) {
				PropertyData nextSuperData = pathMap.get(path);
				if (null == nextSuperData) {
					String name = key;
					String description = "";
					String type = JsonDataType.Object.type();
					String className = "map";

					nextSuperData = new PropertyData();
					nextSuperData.setName(name);
					nextSuperData.setType(type);
					nextSuperData.setDescription(description);
					nextSuperData.setClassName(className);

					pathMap.put(key, nextSuperData);

					List<PropertyData> nodes = superData.getNodes();
					if (null == nodes) {
						nodes = new ArrayList<>();
						superData.setNodes(nodes);
					}
					nodes.add(nextSuperData);
				}

				JSONObject jo = (JSONObject) v;
				setNode(key, path, pathMap, jo, nextSuperData, data);
			} else {
				data.setName(key);
				List<PropertyData> nodes = superData.getNodes();
				if (null == nodes) {
					nodes = new ArrayList<>();
					superData.setNodes(nodes);
				}
				nodes.add(data);
			}
		}
	}
}
